import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui show Gradient;

class BubbleBackgroundChat extends StatelessWidget {
  const BubbleBackgroundChat({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('渐变背景绘制'),
        systemOverlayStyle: SystemUiOverlayStyle.light,
        backgroundColor: Colors.black87,
      ),
      body: ListView.builder(
        //addAutomaticKeepAlives: false,
        addRepaintBoundaries: false,
        itemCount: 30,
        itemBuilder: (context, index) {
          double margin = 15;
          final messageIsMine = index % 3 == 0;
          double marginLeft = messageIsMine ? 60 : margin;
          double marginRight = messageIsMine ? margin : 60;
          return Container(
            margin:
                EdgeInsets.fromLTRB(marginLeft, margin, marginRight, margin),
            padding: EdgeInsets.all(5),
            alignment:
                messageIsMine ? Alignment.centerRight : Alignment.centerLeft,
            child: GradientBackgroundBubble(
              colors: messageIsMine
                  ? const [Color(0xFFA9A6A9), Color(0xFF3A364B)]
                  : const [Color(0xFF19B7FF), Color(0xFFFF683B)],
              scrollableState: Scrollable.of(context)!,
              child: Padding(
                padding: const EdgeInsets.all(15.0),
                child: Text(
                  'Hello Message $index, Hello Hello Hello , Hello Hello Hello,Hello Hello Hello , Hello Hello Hello',
                  style: const TextStyle(
                    fontSize: 18.0,
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

class GradientBackgroundBubble extends StatelessWidget {
  final List<Color> colors;
  final Widget child;
  final ScrollableState scrollableState;
  const GradientBackgroundBubble({
    Key? key,
    required this.colors,
    required this.child,
    required this.scrollableState,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: BubbleBackgroundPainter(
        colors: colors,
        scrollableState: scrollableState,
        context: context,
      ),
      child: child,
    );
  }
}

class BubbleBackgroundPainter extends CustomPainter {
  final List<Color> colors;
  final ScrollableState scrollableState;
  final BuildContext context;
  const BubbleBackgroundPainter({
    Key? key,
    required this.colors,
    required this.scrollableState,
    required this.context,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final scrollableBox =
        scrollableState.context.findRenderObject() as RenderBox;
    final bubbleBox = context.findRenderObject() as RenderBox;
    final origin =
        bubbleBox.localToGlobal(Offset.zero, ancestor: scrollableBox);

    final scrollableRect = Offset.zero & scrollableBox.size;
    final paint = Paint()
      ..shader = LinearGradient(
        begin: Alignment.topCenter,
        end: Alignment.bottomCenter,
        colors: colors,
        transform: ScrollGradientTransform(
          -origin.dx,
          -origin.dy,
          0.0,
        ),
      ).createShader(scrollableRect);
    // ..shader = ui.Gradient.linear(
    //   scrollableRect.topCenter,
    //   scrollableRect.bottomCenter,
    //   colors,
    //   [0, 1.0],
    //   TileMode.clamp,
    //   Matrix4.translationValues(-origin.dx, -origin.dy, 0.0).storage,
    // );

    canvas.drawRRect(
        RRect.fromRectAndRadius(
          Offset.zero & size,
          Radius.circular(10.0),
        ),
        paint);
  }

  @override
  bool shouldRepaint(covariant BubbleBackgroundPainter oldDelegate) {
    return oldDelegate.scrollableState != scrollableState ||
        oldDelegate.context != context ||
        oldDelegate.colors != colors;
  }
}

class ScrollGradientTransform extends GradientTransform {
  const ScrollGradientTransform(this.dx, this.dy, this.dz);

  final double dx, dy, dz;

  @override
  Matrix4 transform(Rect bounds, {TextDirection? textDirection}) {
    return Matrix4.identity()..translate(dx, dy, dz);
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    if (other.runtimeType != runtimeType) return false;
    return other is ScrollGradientTransform &&
        other.dx == dx &&
        other.dy == dy &&
        other.dz == dz;
  }

  @override
  int get hashCode => dx.hashCode | dy.hashCode | dz.hashCode;

  @override
  String toString() {
    return '${objectRuntimeType(this, 'ScrollGradientTransform')}(dx: ${debugFormatDouble(dx)}, dy: ${debugFormatDouble(dy)}, dz: ${debugFormatDouble(dz)})';
  }
}
